package com.yibo.redis.util;

import com.yibo.support.helper.ObjectHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * redis的增删改查
 *
 * @author zhanglei
 */
@Service
@SuppressWarnings("unchecked")
public class YBRedis implements RedisCommand<String,Object> {


    @Autowired
    @Qualifier(value = "redisTemplate")
    private RedisTemplate redisTemplate;

    @Override
    public Set<String> getAllKeys() {
        return redisTemplate.keys("*");
    }

    @Override
    public Set<String> getKeysPattern(String pattern) {
        return redisTemplate.keys(pattern);
    }

    @Override
    public Map<String, Object> getAllString() {
        Set<String> keys = getAllKeys();
        Map<String,Object> map = new HashMap<>();
        for(String key : keys){
            if(getType(key) == DataType.STRING) {
                Object value = get(key);
                map.put(key, value);
            }
        }
        return map;
    }

    @Override
    public Map<String, Set<Object>> getAllSet() {
        Set<String> keys = getAllKeys();
        Map<String,Set<Object>>  map = new HashMap<>();

        for(String key : keys){

            if(getType(key) == DataType.SET){
                Set<Object>  set = getSet(key);
                map.put(key,set);
            }
        }
        return map;
    }

    @Override
    public Map<String, Set<Object>> getAllZSetReverseRange() {
        Set<String> keys = getAllKeys();
        Map<String,Set<Object>>  map = new HashMap<>();

        for(String key : keys){

            if(getType(key) == DataType.ZSET){

                Set<Object> objects = getZSetReverseRange(key);
                map.put(key,objects);
            }
        }
        return map;
    }

    @Override
    public Map<String, Set<Object>> getAllZSetRange() {
        Set<String> keys = getAllKeys();
        Map<String,Set<Object>>  map = new HashMap<>();

        for(String key : keys){

            if(getType(key) == DataType.ZSET){

                Set<Object> objects = getZSetRange(key);
                map.put(key,objects);
            }
        }
        return map;
    }

    @Override
    public Map<String, List<Object>> getAllList() {
        Set<String> keys = getAllKeys();
        Map<String,List<Object>>  map = new HashMap<>();

        for(String key : keys){

            if(getType(key) == DataType.LIST){
                List<Object> list = getList(key);
                map.put(key,list);
            }
        }
        return map;
    }

    @Override
    public Map<String, Map<String, Object>> getAllMap() {
        Set<String> keys = getAllKeys();
        Map<String,Map<String,Object>> map = new HashMap<>();

        for(String key : keys){

            if(getType(key) == DataType.HASH){
                map.put(key,getMap(key));
            }
        }
        return map;
    }

    @Override
    public void addList(String key, List<Object> objectList) {

        for(Object object : objectList) {
            addList(key,object);
        }
    }

    @Override
    public long addList(String key, Object value) {
        return redisTemplate.boundListOps(key).rightPush(value);
    }

    @Override
    public long addList(String key, Object... obj) {
        return redisTemplate.boundListOps(key).rightPushAll(obj);
    }

    @Override
    public List<Object> getList(String key, long start, long end) {
        return redisTemplate.boundListOps(key).range(start,end);
    }

    @Override
    public List<Object> getList(String key) {
        return redisTemplate.boundListOps(key).range(0,getListSize(key));
    }

    @Override
    public long getListSize(String key) {
        return redisTemplate.boundListOps(key).size();
    }

    @Override
    public long removeListValue(String key, Object value) {
        return redisTemplate.boundListOps(key).remove(0,value);
    }

    @Override
    public long removeListValue(String key, Object... value) {
        long r = 0;
        if(value != null){

            for(Object object : value){
                r += removeListValue(key,object);
            }
        }
        return r;
    }

    @Override
    public void addListLeft(String key, Object... value) {
        redisTemplate.boundListOps(key).leftPushAll(value);
    }

    @Override
    public void addListRight(String key, Object... value) {
        redisTemplate.boundListOps(key).rightPushAll(value);
    }

    @Override
    public void remove(String... keys) {
        if(keys != null && keys.length != 0){

            if(keys.length == 1){
                redisTemplate.delete(keys[0]);
            }else{
                redisTemplate.delete(Arrays.asList(keys));
            }
        }
    }

    @Override
    public void remove(String key) {
        redisTemplate.delete(key);
    }

    @Override
    public void removeZSetRangeByScore(String key, double start, double end) {
        redisTemplate.boundZSetOps(key).removeRangeByScore(start,end);
    }

    @Override
    public Boolean setSetExpireTime(String key, Long time) {
        return redisTemplate.boundSetOps(key).expire(time,TimeUnit.SECONDS);
    }

    @Override
    public Boolean setZSetExpireTime(String key, Long time) {
        return redisTemplate.boundZSetOps(key).expire(time,TimeUnit.SECONDS);
    }

    @Override
    public boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }

    @Override
    public Object get(String key) {
        return redisTemplate.boundValueOps(key).get();
    }

    @Override
    public Object getAuto(String key) {
        DataType dataType = redisTemplate.type(key);

        if(dataType.code().equals(DataType.ZSET.code())){
            return this.getZSetRange(key);
        }else if(DataType.SET.code().equals(dataType.code())){
            return this.getSet(key);
        }else if(DataType.LIST.code().equals(dataType.code())){
            return this.getList(key);
        }else if(DataType.STRING.code().equals(dataType.code())){
            return this.get(key);
        }else if(DataType.HASH.code().equals(dataType.code())){
            return this.getMap(key);
        }
        return null;
    }

    @Override
    public List<Object> get(String... key) {
        List<Object> objects = new LinkedList<>();
        if(key != null){
            for(String k : key){
                objects.add(get(k));
            }
        }
        return objects;
    }

    @Override
    public List<Object> getByRegular(String regKey) {
        List<Object> result = new LinkedList<>();

        Set<String> keys = getAllKeys();
        for(String key : keys){

            if(Pattern.compile(regKey).matcher(key).matches()
                    && getType(key) == DataType.STRING){
                result.add(get(key));
            }
        }

        return result;
    }

    @Override
    public List<Object> getByPattern(String pattern) {
        List<Object> result = new LinkedList<>();
        Set<String> keys = redisTemplate.keys(pattern);
        if(keys != null) {
            for (String key : keys) {
                result.add(get(key));
            }
        }
        return result;
    }

    @Override
    public List<Object> getMapByPattern(String pattern) {
        Set<String> keys = redisTemplate.keys(pattern);
        List<Object> result = new ArrayList<>();

        if(keys != null){
            for(String key : keys){
                Map<String,Object> map = getMap(key);
                if(map != null) {
                    result.add(map);
                }
            }
        }
        return result;
    }

    @Override
    public void set(String key, Object value) {
        redisTemplate.boundValueOps(key).set(value);
    }

    @Override
    public void set(String key, Object value, Long expireTime) {
        redisTemplate.boundValueOps(key).set(value,expireTime,TimeUnit.SECONDS);
    }

    @Override
    public boolean setExpireTime(String key, Long expireTime) {
        return redisTemplate.boundValueOps(key).expire(expireTime,TimeUnit.SECONDS);
    }

    @Override
    public DataType getType(String key) {
        return redisTemplate.type(key);
    }

    @Override
    public void removeMapField(String key, Object... field) {
        redisTemplate.boundHashOps(key).delete(field);
    }

    @Override
    public Map<String, Object> getMap(String key) {
        return redisTemplate.boundHashOps(key).entries();
    }

    @Override
    public Long getMapSize(String key) {
        return redisTemplate.boundHashOps(key).size();
    }

    @Override
    public <T> T getMapField(String key, String field) {
        return (T) redisTemplate.boundHashOps(key).get(field);
    }

    @Override
    public boolean hasMapKey(String key, String field) {
        return redisTemplate.boundHashOps(key).hasKey(field);
    }

    @Override
    public List<Object> getMapFieldValue(String key) {
        return redisTemplate.boundHashOps(key).values();
    }

    @Override
    public Set<Object> getMapFieldKey(String key) {
        return redisTemplate.boundHashOps(key).keys();
    }

    @Override
    public long setMapIncrby(String key, String field, long increment) {
        return redisTemplate.boundHashOps(key).increment(field,increment);
    }

    @Override
    public double setMapIncrbyFloat(String key, String field, double increment) {
        return redisTemplate.boundHashOps(key).increment(field,increment);
    }

    @Override
    public void addAuto(String key, Object value) {
        DataType type = getType(key);

        if(DataType.STRING == type){
            set(key,value);

        }else if(DataType.LIST == type){
            remove(key);
            addList(key,value);
        }else if(DataType.SET == type){
            remove(key);
            addSet(key,value);
        }else if(DataType.ZSET == type){
            remove(key);
            Set<ZSetOperations.TypedTuple<Object>>  ret = getZSetReverseRangeWithScore(key);
            if(!ObjectHelper.isEmpty(ret)){
                ZSetOperations.TypedTuple<Object> one = ret.iterator().next();
                redisTemplate.boundZSetOps(key).add(value,one.getScore()+1);
            }
        }else if(DataType.HASH == type){
            remove(key);
            addMap(key, (Map<String, Object>) value);
        }
    }

    @Override
    public void setAuto(String key, Object value) {
        addAuto(key,value);
    }

    @Override
    public void addMap(String key, String field, Object value) {
        redisTemplate.boundHashOps(key).put(field,value);
    }

    @Override
    public void addMap(String key, Map<String, Object> map) {
        redisTemplate.boundHashOps(key).putAll(map);
    }

    @Override
    public void addMap(String key, String field, Object value, long time) {
        redisTemplate.boundHashOps(key).put(field,value);
        redisTemplate.boundHashOps(key).expire(time,TimeUnit.SECONDS);
    }

    @Override
    public void addSet(String key, Object... obj) {
        redisTemplate.boundSetOps(key).add(obj);
    }

    @Override
    public void addSet(String key, Set<Object> obj) {
        if(obj != null) {
            redisTemplate.boundSetOps(key).add(obj.toArray());
        }
    }

    @Override
    public void watch(String key) {
        redisTemplate.watch(key);
    }

    @Override
    public long removeSetValue(String key, Object value) {
        return redisTemplate.boundSetOps(key).remove(value);
    }

    @Override
    public long removeSetValue(String key, Object... value) {
        return redisTemplate.boundSetOps(key).remove(value);
    }

    @Override
    public long getSetSize(String key) {
        return redisTemplate.boundSetOps(key).size();
    }

    @Override
    public boolean hasSetValue(String key, Object value) {
        return redisTemplate.boundSetOps(key).isMember(value);
    }

    @Override
    public Set<Object> getSet(String key) {
        return redisTemplate.boundSetOps(key).members();
    }

    @Override
    public Set<Object> getSetUnion(String... key) {
        Set<Object> result = new HashSet<>();

        if(key != null){

            int len = key.length;
            for(int i=0 ; i<len ; i++){
                String k = key[i];

                if(i+1 < len){
                    String k1 = key[i+1];
                    Set<Object> un = redisTemplate.boundSetOps(k).union(k1);
                    result.addAll(un);
                }

            }
        }
        return result;
    }

    @Override
    public Set<Object> getSetUnion(String key, Set<Object> set) {
        return redisTemplate.boundSetOps(key).union(set);
    }

    @Override
    public Set<Object> getIntersect(String... key) {
        Set<Object> result = new HashSet<>();
        if(key != null){

            int len = key.length;
            for(int i=0 ; i<len ; i++){
                String k = key[i];

                if(i+1 < len){
                    String k1 = key[i+1];
                    Set<Object> un = redisTemplate.boundSetOps(k).intersect(k1);
                    result.addAll(un);
                }
            }
        }
        return result;
    }

    @Override
    public Set<Object> getIntersect(String key, Set<Object> set) {
        return redisTemplate.boundSetOps(key).intersect(set);
    }

    @Override
    public void removeBlear(String... blears) {
        if(blears != null){

            for(String blear : blears){
                redisTemplate.delete(redisTemplate.keys(blear));
            }
        }
    }

    @Override
    public boolean renameIfExist(String oldKey, String newKey) {
        return redisTemplate.renameIfAbsent(oldKey,newKey);
    }

    @Override
    public void removeByRegular(String... reg) {
        if(reg != null){

            for(String r : reg){
                Set<String> keys = getAllKeys();

                for(String key : keys){
                    if(Pattern.compile(r).matcher(key).matches()){

                        redisTemplate.delete(key);
                    }
                }
            }
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void removeMapFieldByRegular(String key, String... reg) {
        if(reg == null){
            return ;
        }
        Map<String,Object> objectMap = getMap(key);
        Set<String> keys = new HashSet<>();

        for(Map.Entry<String,Object> map : objectMap.entrySet()){
            String k = map.getKey();

            for(String r : reg) {
                if (Pattern.compile(r).matcher(k).matches()){
                    keys.add(k);
                }
            }
        }

        redisTemplate.boundHashOps(key).delete(keys.toArray());
    }

    @Override
    public Long removeZSetValue(String key, Object... value) {
        return redisTemplate.boundZSetOps(key).remove(value);
    }

    @Override
    public void removeZSet(String key) {
        redisTemplate.delete(key);
    }

    @Override
    public void removeZSetRange(String key, Long start, Long end) {
        redisTemplate.boundZSetOps(key).removeRange(start,end);
    }

    @Override
    public void setZSetUnionAndStore(String key, String key1, String key2) {
        redisTemplate.boundZSetOps(key).unionAndStore(key1,key2);
    }

    @Override
    public <T> T getZSetRange(String key) {
        return getZSetRange(key,0,getZSetSize(key));
    }

    @Override
    public <T> T getZSetRange(String key, long start, long end) {
        return (T) redisTemplate.boundZSetOps(key).range(start,end);
    }

    @Override
    public Set<Object> getZSetReverseRange(String key) {
        return getZSetReverseRange(key,0,getZSetSize(key));
    }

    @Override
    public Set<Object> getZSetReverseRange(String key, long start, long end) {
        return redisTemplate.boundZSetOps(key).reverseRange(start,end);
    }

    @Override
    public Set<Object> getZSetRangeByScore(String key, double start, double end) {
        return redisTemplate.boundZSetOps(key).rangeByScore(start,end);
    }

    @Override
    public Set<Object> getZSetReverseRangeByScore(String key, double start, double end) {
        return redisTemplate.boundZSetOps(key).reverseRangeByScore(start,end);
    }

    @Override
    public Set<ZSetOperations.TypedTuple<Object>> getZSetRangeWithScore(String key, long start, long end) {
        return redisTemplate.boundZSetOps(key).rangeByScoreWithScores(start,end);
    }

    @Override
    public Set<ZSetOperations.TypedTuple<Object>> getZSetReverseRangeWithScore(String key, long start, long end) {
        return redisTemplate.boundZSetOps(key).reverseRangeByScoreWithScores(start,end);
    }

    @Override
    public Set<ZSetOperations.TypedTuple<Object>> getZSetRangeWithScore(String key) {
        return redisTemplate.boundZSetOps(key).rangeByScoreWithScores(0,getZSetSize(key));
    }

    @Override
    public Set<ZSetOperations.TypedTuple<Object>> getZSetReverseRangeWithScore(String key) {
        return redisTemplate.boundZSetOps(key).reverseRangeByScoreWithScores(0,getZSetSize(key));
    }

    @Override
    public long getZSetCountSize(String key, double start, double end) {
        return redisTemplate.boundZSetOps(key).count(start,end);
    }

    @Override
    public long getZSetSize(String key) {
        return redisTemplate.boundZSetOps(key).size();
    }

    @Override
    public double getZSetScore(String key, Object value) {
        return redisTemplate.boundZSetOps(key).score(value);
    }

    @Override
    public double incrementZSetScore(String key, Object value, double delta) {
        return redisTemplate.boundZSetOps(key).incrementScore(value,delta);
    }

    @Override
    public Boolean addZSet(String key, double score, Object value) {
        return redisTemplate.boundZSetOps(key).add(value,score);
    }

    @Override
    public Long addZSet(String key, TreeSet<Object> value) {
        return redisTemplate.boundZSetOps(key).add(value);
    }

    @Override
    public Long addZSet(String key, double[] score, Object[] value) {
        long re = 0;

        if(score != null && value != null && score.length == value.length){
            int len = score.length;
            for(int i=0 ; i<len; i++){
                re += redisTemplate.boundZSetOps(key).add(value[i],score[i]) ? 1 : 0;
            }
        }
        return re;
    }
}
